import { isObject } from "../util";
import { arrayMethods } from "./array";
import Dep from "./dep";

class Observer {
  // 需要对value的属性描述重新定义
  constructor(value) {
    // 需要对value的属性描述重新定义
    // 对数组要分开处理， 不使用 defineProperty 进行代理，会影响性能
    this.dep = new Dep(); // 给对象或数组本身加个 dep, 实际上只有数组才用得上，对象因为有递归，用不上
    // value.__ob__ = this; // 给 value 加个属性，会产生死循环
    // 如果数据上有 __ob__ 属性，说明被观测过了
    // 用此方法代替上一行，并解决死循环, __ob__ 属性后面需要被用到
    Object.defineProperty(value, "__ob__", {
      value: this,
      enumerable: false, // 不能枚举此属性，不能被循环
      configurable: false, // 不能被删除此属性
    });

    if (Array.isArray(value)) {
      value.__proto__ = arrayMethods; // 重写 value 原型上能引起数组变化的 7个方法，不影响 Array 原型上原来的方法
      // Object.setPrototypeOf(value, arrayMethods); // 与上一行等同
      // 如果浏览器还不兼容，就循环赋值来设置给 value

      this.observeArray(value); // 如果数组中某项是对象，也需要响应式，此处会遍历，有时会影响性能，所以可能需要使用 Object.freeze()来冻结
    } else {
      this.walk(value); // 初始化的时候就对数据进行监控
    }
  }
  observeArray(array) {
    for (let i = 0; i < array.length; i++) {
      observe(array[i]);
    }
  }
  walk(data) {
    Object.keys(data).forEach((key) => {
      defineReactive(data, key, data[key]);
    });
  }
}

// 对里层属性进行监控
function dependArray(arr) {
  for (let i = 0; i < arr.length; i++) {
    const current = arr[i];
    current.__ob__ && current.__ob__.dep.depend(); // 让里层数据和外层数据收集同一个watcher
    if (Array.isArray(current)) {
      dependArray(current);
    }
  }
}

//  此处的 value 是个闭包
//  类似于 vm[key].get = function(){return  value}
// 此方法值处理对象，不处理数组
function defineReactive(data, key, value) {
  // value 可能是一个对象,要继续劫持，数据嵌套尽量不要太深，可能会导致性能问题
  // 给对象的属性添加 dep, 当 value 是对象或数组时，childOb 都会是一个Observer 实例，有一个 dep 属性
  let childOb = observe(value);
  let dep = new Dep();
  Object.defineProperty(data, key, {
    get() {
      // 不依赖于模板的取值不会收集依赖产生 watcher
      // 如果有 watcher，就让 watcher 记住 dep，防止产生重复的 dep, 同时 dep 就收集此 watcher
      if (Dep.target) {
        dep.depend(); // dep 被 watcher 收集
        if (childOb) {
          childOb.dep.depend(); //
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }

      return value;
    },
    set(newVal) {
      if (value === newVal) return;
      observe(newVal); // 如果新值是个对象，继续拦截
      value = newVal; // 不能使用 data[key] = newVal, 会产生死循环
      dep.notify(); // 通知收集的 watcher 去执行
    },
  });
}

export default function observe(data) {
  if (!isObject(data)) return;
  if (data.__ob__) return; // 被拦截过的不需要再次拦截
  // 通过类来实现数据的观测，方便扩展，生成实例
  return new Observer(data);
}
